package dao

import (
	"context"
	"errors"
	"github.com/google/uuid"
	open_pb "ldaccount/api/pb/client/open/api/open/v1"
	c_pb "ldaccount/api/pb/client/sms"
	"ldaccount/pkg/rpc"
	"time"
)

type SmsRepo interface {
	// CanMsgBeConsumed message consume check
	CanMsgBeConsumed(ctx context.Context, digest string) bool
	// RollbackCacheConsumedMsg 缓存已经消费的消息
	RollbackCacheConsumedMsg(ctx context.Context, digest string) error
	// MailMsgCache 邮件消息缓存
	MailMsgCache(ctx context.Context, digest string) bool

	// SendMobileCode 发送短信验证码
	SendMobileCode(ctx context.Context, mobile string, codeType string) error
	// VerifyMobileCode code 验证
	VerifyMobileCode(ctx context.Context, mobile, code, codeType string) (bool, error)
	// SendMpNewMessage 发送公众号图文消息
	SendMpNewMessage(ctx context.Context, appId, openId, title, desc, picUrl, url string) error
}

type smsDaoImpl struct {
	*Dao
	smsClientPool  *rpc.Pool
	openClientPool *rpc.Pool
	name           string
}

var smsRepo SmsRepo

// GetSmsRepo return sms repo
func GetSmsRepo() SmsRepo {
	return smsRepo
}

// InitSmsRepo injects InitSmsRepo with implementation
func InitSmsRepo(a SmsRepo) {
	smsRepo = a
}

// NewSmsRepo initialize
func NewSmsRepo(openPool, smsPool *rpc.Pool) *smsDaoImpl {
	return &smsDaoImpl{
		Dao:            dao,
		openClientPool: openPool,
		smsClientPool:  smsPool,
		name:           "dao_sms",
	}
}

// CanMsgBeConsumed message consume check
func (dao *smsDaoImpl) CanMsgBeConsumed(ctx context.Context, digest string) bool {
	key := dao.BuildCacheKey("consumed_msg", digest)
	ok, err := dao.cache.SetNX(ctx, key, "ok", 1*time.Hour)
	if err != nil {
		return true
	}
	return ok
}

// RollbackCacheConsumedMsg 缓存已经消费的消息
func (dao *smsDaoImpl) RollbackCacheConsumedMsg(ctx context.Context, digest string) error {
	key := dao.BuildCacheKey("consumed_msg", digest)
	_, err := dao.cache.Del(ctx, key)
	return err
}

// MailMsgCache 邮件消息缓存
func (dao *smsDaoImpl) MailMsgCache(ctx context.Context, digest string) bool {
	key := dao.BuildCacheKey("sent_mail", digest)
	ok, err := dao.cache.SetNX(ctx, key, "ok", 10*time.Minute)
	if err != nil {
		return true
	}
	return ok
}

// SendMobileCode 发送短信验证码
func (s *smsDaoImpl) SendMobileCode(ctx context.Context, mobile string, codeType string) error {
	if mobile == "" || codeType == "" {
		return errors.New("codeType, mobile is null")
	}

	var rID string
	rIDi := ctx.Value("_requestID")
	if rIDi != nil {
		rID = rIDi.(string)
	} else {
		uid, _ := uuid.NewUUID()
		rID = uid.String()
	}
	body := map[string]interface{}{
		"request_id":   rID,
		"phone":        mobile,
		"appid":        "bilin-account",
		"message_type": codeType,
	}
	bodyB, err := json.Marshal(body)
	if err != nil {
		return err
	}
	return s.nsq.Publish("gsms_verify_send", bodyB)
}

// VerifyMobileCode code 验证
func (s *smsDaoImpl) VerifyMobileCode(ctx context.Context, mobile, code, codeType string) (bool, error) {
	conn, err := s.smsClientPool.Get(ctx)
	if err != nil {
		return false, err
	}
	defer func() {
		_ = conn.Close()
	}()
	client := c_pb.NewSMSClient(conn.ClientConn)
	req := &c_pb.VerifyReq{
		Phone:       mobile,
		AppId:       "bilin-account",
		MessageType: codeType,
		Code:        code,
	}
	resp, err := client.CodeVerify(ctx, req)
	if err != nil {
		return false, err
	}
	return resp.VerifyResult, nil
}

// SendMpNewMessage 发送公众号图文消息
func (s *smsDaoImpl) SendMpNewMessage(ctx context.Context, appId, openId, title, desc, picUrl, url string) error {
	news := &open_pb.News{
		Title:       title,
		Description: desc,
		Url:         url,
		PicUrl:      picUrl,
	}
	req := &open_pb.SendWechatCustomMessageRequest{
		ToUser:  openId,
		MsgType: "news",
		News:    news,
		AppId:   appId,
	}
	conn, err := s.openClientPool.Get(ctx)
	if err != nil {
		return err
	}
	defer func() {
		_ = conn.Close()
	}()
	client := open_pb.NewOpenServiceClient(conn.ClientConn)
	_, err = client.SendWechatCustomMessage(ctx, req)
	return err
}
